﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;
using IanUtil.DB;

namespace IanUtil.ORM
{
    public class IU_ORM_Model
    {
        private StringBuilder whereString;

        public IU_ORM_Model()
        {
            whereString = new StringBuilder();
        }

        /// <summary>
        /// 处理DBNull
        /// </summary>
        /// <param name="value"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        private object HandleDBNull(object value, Type t)
        {
            return value == DBNull.Value ? null : value;
        }

        /// <summary>
        /// 获取表特性
        /// </summary>
        /// <returns></returns>
        public IU_ORM_Table GetTableAttr()
        {
            IU_ORM_Table tableAttr = null;
            try
            {
                object[] attrs = this.GetType().GetCustomAttributes(typeof(IU_ORM_Table), false);
                tableAttr = (IU_ORM_Table)attrs[0];
            }
            catch (Exception)
            { }
            return tableAttr;
        }

        /// <summary>
        /// 获取域特性
        /// </summary>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public IU_ORM_Field GetField(string fieldName)
        {
            IU_ORM_Field fieldAttr = null;
            try
            {
                fieldAttr = (IU_ORM_Field)this.GetType().GetProperties().ToList().Find(p => { return p.Name == fieldName; }).GetCustomAttributes(typeof(IU_ORM_Field), false)[0];
            }
            catch (Exception)
            { }
            return fieldAttr;
        }

        /// <summary>
        /// 获取主键值
        /// </summary>
        /// <returns></returns>
        public object GetPKey()
        {
            object pkField = null;
            try
            {
                IU_ORM_Table tableAttr = (IU_ORM_Table)GetTableAttr();

                PropertyInfo[] properties = GetType().GetProperties();
                foreach (PropertyInfo x in properties)
                {
                    IU_ORM_Field fieldAttr = (IU_ORM_Field)x.GetCustomAttributes(false)[0];
                    if (fieldAttr.Name == tableAttr.PKey)
                    {
                        if (!fieldAttr.IsFKey)
                        {
                            pkField = x.GetValue(this, null);
                        }
                        else//外键
                        {
                            pkField = ((IU_ORM_Model)x.GetValue(this, null)).GetPKey();
                        }
                        break;
                    }
                }
            }
            catch (Exception)
            {
            }
            return pkField;
        }

        /// <summary>
        /// 根据主键获取记录
        /// </summary>
        /// <returns></returns>
        public virtual IU_ORM_Model Get(object pkValue)
        {
            IU_ORM_Model ret = null;

            try
            {
                IU_DB_DataBase db = ((Dictionary<string, IU_DB_DataBase>)typeof(IU_ORM_DBSetting).GetField("DBs").GetValue(null))[GetTableAttr().Source];
                PropertyInfo pPkey = this.GetType().GetProperties().ToList().Find(x => { return this.GetField(x.Name).Name == this.GetTableAttr().PKey; });
                string tableName = this.GetTableAttr().Name;
                string sql = "select * from {0} where {1}='{2}'";
                sql = string.Format(sql, tableName, this.GetTableAttr().PKey, pkValue);
                DataRow row = db.SQLForDataSet(sql).Tables[0].Rows[0];

                foreach (PropertyInfo p in this.GetType().GetProperties())
                {
                    IU_ORM_Field fieldAttr = this.GetField(p.Name);
                    if (fieldAttr != null)
                    {
                        if (!fieldAttr.IsFKey)
                        {
                            p.SetValue(this, HandleDBNull(row[fieldAttr.Name], p.PropertyType), null);
                        }
                        else
                        {
                            object fobj = Activator.CreateInstance(p.PropertyType);
                            fobj = ((IU_ORM_Model)fobj).Get(HandleDBNull(row[fieldAttr.Name], p.PropertyType));
                            p.SetValue(this, fobj, null);
                        }
                    }
                }
                ret = this;
            }
            catch
            {

            }
            return ret;
        }

        /// <summary>
        /// 获取所有记录
        /// </summary>
        /// <returns></returns>
        public virtual List<IU_ORM_Model> GetAll()
        {
            ClearWhere();
            this.whereString.Append(" and 1=1");
            return GetWhere();
        }

        /// <summary>
        /// 清除查询条件
        /// </summary>
        public void ClearWhere()
        {
            this.whereString.Clear();
        }

        /// <summary>
        /// 设置等于条件
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public IU_ORM_Model Equals(string fieldName, object value)
        {
            try
            {
                string tpl = " and {0}={1}";
                string fieldDBName = this.GetField(fieldName).Name;
                if (value == null)
                {
                    tpl = string.Format(tpl, fieldDBName, "NULL");
                }
                else
                {
                    if (!this.GetField(fieldName).IsFKey)//非外键情况
                    {
                        tpl = string.Format(tpl, fieldDBName, "'" + value + "'");
                    }
                    else//外键情况
                    {
                        tpl = string.Format(tpl, fieldDBName, "'" + ((IU_ORM_Model)value).GetPKey() + "'");
                    }
                }
                whereString.Append(tpl);
            }
            catch (Exception)
            {
            }

            return this;
        }

        /// <summary>
        /// 设置类似条件
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="likePattern"></param>
        /// <returns></returns>
        public IU_ORM_Model Like(string fieldName, string likePattern)
        {
            try
            {
                string tpl = " and {0} like '{1}'";
                string fieldDBName = this.GetField(fieldName).Name;
                tpl = string.Format(tpl, fieldDBName, likePattern);
                whereString.Append(tpl);
            }
            catch (Exception)
            {
            }

            return this;
        }

        /// <summary>
        /// 设置大于条件
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value"></param>
        /// <param name="isCanEqual"></param>
        /// <returns></returns>
        public IU_ORM_Model Greater(string fieldName, object value, bool isCanEqual)
        {
            try
            {
                string tpl = " and {0}{1}'{2}'";
                string opt = isCanEqual ? ">=" : ">";
                string fieldDBName = this.GetField(fieldName).Name;
                if (!this.GetField(fieldName).IsFKey)//非外键情况
                {
                    tpl = string.Format(tpl, fieldDBName, opt, value);
                }
                else//外键情况
                {
                    tpl = string.Format(tpl, fieldDBName, opt, ((IU_ORM_Model)value).GetPKey());
                }
                whereString.Append(tpl);
            }
            catch (Exception)
            {
            }

            return this;
        }

        /// <summary>
        /// 设置小于条件
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="value"></param>
        /// <param name="isCanEqual"></param>
        /// <returns></returns>
        public IU_ORM_Model Less(string fieldName, object value, bool isCanEqual)
        {
            try
            {
                string tpl = " and {0}{1}'{2}'";
                string opt = isCanEqual ? "<=" : "<";
                string fieldDBName = this.GetField(fieldName).Name;
                if (!this.GetField(fieldName).IsFKey)//非外键情况
                {
                    tpl = string.Format(tpl, fieldDBName, opt, value);
                }
                else//外键情况
                {
                    tpl = string.Format(tpl, fieldDBName, opt, ((IU_ORM_Model)value).GetPKey());
                }
                whereString.Append(tpl);
            }
            catch (Exception)
            {
            }

            return this;
        }

        /// <summary>
        /// 设置范围条件
        /// </summary>
        /// <returns></returns>
        public IU_ORM_Model Between(string fieldName, object minValue, object maxValue)
        {
            try
            {
                string tpl = " and ({0} between '{1}' and '{2}')";
                string fieldDBName = this.GetField(fieldName).Name;
                if (!this.GetField(fieldName).IsFKey)//非外键情况
                {
                    tpl = string.Format(tpl, fieldDBName, minValue, maxValue);
                }
                else//外键情况
                {
                    tpl = string.Format(tpl, fieldDBName, ((IU_ORM_Model)minValue).GetPKey(), ((IU_ORM_Model)maxValue).GetPKey());
                }
                whereString.Append(tpl);
            }
            catch (Exception)
            {
            }

            return this;
        }

        /// <summary>
        /// 根据条件查找返回首个符合条件的结果
        /// </summary>
        /// <returns></returns>
        public virtual IU_ORM_Model GetWhereFirst()
        {
            var list = GetWhere();
            return list.Count > 0 ? list[0] : null;
        }

        /// <summary>
        /// 根据条件查找
        /// </summary>
        /// <returns></returns>
        public virtual List<IU_ORM_Model> GetWhere()
        {
            List<IU_ORM_Model> list = new List<IU_ORM_Model>();
            try
            {
                if (this.whereString.Length == 0)//查询语句为空直接退出
                {
                    return list;
                }

                IU_DB_DataBase db = ((Dictionary<string, IU_DB_DataBase>)typeof(IU_ORM_DBSetting).GetField("DBs").GetValue(null))[GetTableAttr().Source];
                string sql = "select * from {0} where 1=1{1}";
                sql = string.Format(sql, GetTableAttr().Name, this.whereString);
                DataSet ds = db.SQLForDataSet(sql);
                list = new List<IU_ORM_Model>();
                foreach (DataRow row in ds.Tables[0].Rows)
                {
                    object obj = Activator.CreateInstance(this.GetType());
                    foreach (DataColumn column in ds.Tables[0].Columns)
                    {
                        foreach (PropertyInfo propertyInfo in obj.GetType().GetProperties())
                        {
                            object[] attrs = propertyInfo.GetCustomAttributes(false);
                            if (attrs != null)
                            {
                                IU_ORM_Field fieldAttr = (IU_ORM_Field)attrs[0];
                                if (fieldAttr.Name == column.ColumnName)
                                {
                                    if (!fieldAttr.IsFKey)
                                    {
                                        propertyInfo.SetValue(obj, HandleDBNull(row[column], propertyInfo.PropertyType), null);
                                    }
                                    else//外键情况
                                    {
                                        object fobj = Activator.CreateInstance(propertyInfo.PropertyType);
                                        string pKey = ((IU_ORM_Model)fobj).GetTableAttr().PKey;

                                        PropertyInfo p = propertyInfo.PropertyType.GetProperties().ToList().Find(item =>
                                        {
                                            return ((IU_ORM_Field)item.GetCustomAttributes(false)[0]).Name == pKey;
                                        });
                                        //p.SetValue(fobj, HandleDBNull(row[column], p.PropertyType), null);
                                        fobj = ((IU_ORM_Model)fobj).Get(HandleDBNull(row[column], p.PropertyType));
                                        propertyInfo.SetValue(obj, fobj, null);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                    list.Add((IU_ORM_Model)obj);
                }
            }
            catch (Exception)
            {
            }
            finally
            {
                ClearWhere();
            }

            return list;
        }

        /// <summary>
        /// 插入记录
        /// </summary>
        /// <returns></returns>
        public virtual bool Insert()
        {
            bool ret = false;
            try
            {
                IU_DB_DataBase db = ((Dictionary<string, IU_DB_DataBase>)typeof(IU_ORM_DBSetting).GetField("DBs").GetValue(null))[GetTableAttr().Source];
                string sql = "insert into {0} ({1}) values ({2})";
                PropertyInfo[] properties = this.GetType().GetProperties();
                StringBuilder sbFields = new StringBuilder();
                StringBuilder sbValues = new StringBuilder();
                foreach (PropertyInfo propertyInfo in properties)
                {
                    object[] attrs = propertyInfo.GetCustomAttributes(false);
                    if (attrs != null)
                    {
                        IU_ORM_Field fieldAttr = (IU_ORM_Field)attrs[0];
                        if (!fieldAttr.IsAuto)
                        {
                            sbFields.Append(fieldAttr.Name + ",");
                            if (!fieldAttr.IsFKey)
                            {
                                object value = propertyInfo.GetValue(this, null);
                                sbValues.Append((value == null ? "NULL" : "'" + value + "'") + ",");
                            }
                            else//外键情况
                            {
                                IU_ORM_Model fKey = (IU_ORM_Model)propertyInfo.GetValue(this, null);
                                object fKeyPKey = fKey == null ? "NULL" : "'" + fKey.GetPKey() + "'";
                                sbValues.Append(fKeyPKey + ",");
                            }
                        }
                    }
                }
                sbFields.Remove(sbFields.Length - 1, 1);
                sbValues.Remove(sbValues.Length - 1, 1);
                sql = string.Format(sql, GetTableAttr().Name, sbFields.ToString(), sbValues.ToString());
                if (db.ExcuteSQL(sql) > 0)
                {
                    ret = true;
                }
                else
                {
                    ret = false;
                }
            }
            catch (Exception)
            {
            }
            return ret;
        }

        /// <summary>
        /// 根据主键删除
        /// </summary>
        /// <returns></returns>
        public virtual bool Delete()
        {
            bool ret = false;
            try
            {
                string tableName = this.GetTableAttr().Name;
                PropertyInfo pkInfo = this.GetType().GetProperties().ToList().Find(pk => { return this.GetField(pk.Name).Name == this.GetTableAttr().PKey; });
                string pkName = this.GetField(pkInfo.Name).Name;
                object pkValue = pkInfo.GetValue(this, null);
                string sql = "delete from {0} where {1}='{2}'";
                sql = string.Format(sql, tableName, pkName, pkValue);

                if (IU_ORM_DBSetting.DBs[this.GetTableAttr().Source].ExcuteSQL(sql) > 0)
                {
                    ret = true;
                }
            }
            catch (Exception)
            { }
            return ret;
        }

        /// <summary>
        /// 根据主键修改
        /// </summary>
        /// <returns></returns>
        public virtual bool Update()
        {
            bool ret = false;
            try
            {
                string tableName = this.GetTableAttr().Name;
                PropertyInfo pkInfo = this.GetType().GetProperties().ToList().Find(pk => { return this.GetField(pk.Name).Name == this.GetTableAttr().PKey; });
                string pkName = this.GetField(pkInfo.Name).Name;
                object pkValue = pkInfo.GetValue(this, null);

                StringBuilder sbSetter = new StringBuilder();

                //this.GetType().GetProperties().ToList().ForEach(p =>

                foreach (PropertyInfo p in this.GetType().GetProperties())
                {
                    if (this.GetField(p.Name) != null && !this.GetField(p.Name).IsAuto)
                    {
                        if (p.Name != pkInfo.Name)
                        {
                            string setter = "{0}={1},";
                            if (!this.GetField(p.Name).IsFKey)//非外键情况
                            {
                                object value = "'" + p.GetValue(this, null) + "'";
                                setter = string.Format(setter, this.GetField(p.Name).Name, value);
                            }
                            else//外键情况
                            {
                                object value = ((IU_ORM_Model)p.GetValue(this, null));
                                value = value == null ? "NULL" : "'" + ((IU_ORM_Model)value).GetPKey() + "'";
                                setter = string.Format(setter, this.GetField(p.Name).Name, value);
                            }
                            sbSetter.Append(setter);
                        }
                    }
                }

                sbSetter.Remove(sbSetter.Length - 1, 1);

                string sql = "update {0} set {1} where {2}='{3}'";
                sql = string.Format(sql, tableName, sbSetter.ToString(), pkName, pkValue);

                if (IU_ORM_DBSetting.DBs[this.GetTableAttr().Source].ExcuteSQL(sql) > 0)
                {
                    ret = true;
                }
            }
            catch (Exception)
            { }

            return ret;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.GetPKey().ToString();
        }
    }
}
